package com.cyanite.playlv2;

import com.cyanite.playlv2.app.LoadableListActivityWithView;
import com.cyanite.playlv2.location.BestLocationListener;
import com.cyanite.playlv2.location.LocationUtils;
import com.cyanite.playlv2.preferences.Preferences;
import com.cyanite.playlv2.util.MenuUtils;
import com.cyanite.playlv2.util.NotificationsUtil;
import com.cyanite.playlv2.util.VenueUtils;
import com.cyanite.playlv2.widget.FocusSubTripListAdapter;
import com.cyanite.playlv2.widget.PlaceCategoryImageAdapter;
import com.cyanite.playlv2.widget.SegmentedButton;
import com.cyanite.playlv2.widget.VenueListAdapter;
import com.cyanite.playlv2.widget.SegmentedButton.OnClickListenerSegmentedButton;
import com.cyanite.playlv2api.PlayLv2Api;
import com.cyanite.playlv2api.error.PlayLv2ApiException;
import com.cyanite.playlv2api.types.Group;
import com.cyanite.playlv2api.types.Task;
import com.cyanite.playlv2api.types.Venue;
import com.cyanite.playlv2api.util.ConstantsUtils.VenueCategoryRes;
import com.cyanite.playlv2.R;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.SearchManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.DialogInterface.OnCancelListener;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Gallery;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;

public class NearbyVenuesActivity extends LoadableListActivityWithView {

    public static final String INTENT_EXTRA_STARTUP_GEOLOC_DELAY = PlayLv2.PACKAGE_NAME
            + ".NearbyVenuesActivity.INTENT_EXTRA_STARTUP_GEOLOC_DELAY";

    private static final int MENU_REFRESH = 0;
    private static final int MENU_ADD_VENUE = 1;
    private static final int MENU_SEARCH = 2;
    private static final int DIALOG_PICK_CATEGORY = 1;
    private static final int DIALOG_ERROR = 2;

    private static final int RESULT_CODE_ACTIVITY_VENUE = 1;

    private StateHolder mStateHolder = new StateHolder();
    private SearchLocationObserver mSearchLocationObserver = new SearchLocationObserver();
    private ListView mListView;
    private VenueListAdapter mVenueListAdapter;
    private FocusSubTripListAdapter mFocusSubTripListAdapter;
    private LinearLayout mFooterView;
    private TextView mTextViewFooter;
    private View mEmptyProgress;
    private Handler mHandler;
    private LinearLayout mEmpty;

    private BroadcastReceiver mLoggedOutReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            finish();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setDefaultKeyMode(Activity.DEFAULT_KEYS_SEARCH_LOCAL);
        registerReceiver(mLoggedOutReceiver, new IntentFilter(PlayLv2.INTENT_ACTION_LOGGED_OUT));
        setContentView(R.layout.nearby_venue_activity);

        ensureUi();

        // Check if we're returning from a configuration change.
        mStateHolder = new StateHolder();
        mStateHolder.setQuery("");

        // Start a new search if one is not running or we have no results.
        long firstLocDelay = 0L;
        if (getIntent().getExtras() != null) {
            firstLocDelay = getIntent().getLongExtra(INTENT_EXTRA_STARTUP_GEOLOC_DELAY, 0L);
        }
        startTask(firstLocDelay);
        populateFooter(mStateHolder.getReverseGeoLoc());
    }

    private void ensureUi(){
        mEmpty = (LinearLayout) findViewById(R.id.llEmptyVenue);
        Gallery gallery = (Gallery)findViewById(R.id.galleryCtrl);
        PlaceCategoryImageAdapter localPlaceCategoryImageAdapter = new PlaceCategoryImageAdapter(this);
        gallery.setAdapter(localPlaceCategoryImageAdapter);
        gallery.setCallbackDuringFling(false);
        gallery.setSelection(0);
        gallery.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener(){
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                    switch (position)   {
                        case 0:
                            mStateHolder.setCategoryId(-1);
                            categoryListView();
                            break;
                        case 1:
                            mStateHolder.setIsTaskPage(true);
                            if (!mStateHolder.getIsRunnedGetTasks()) {
                                mFocusSubTripListAdapter = new FocusSubTripListAdapter(NearbyVenuesActivity.this,
                                        ((PlayLv2) getApplication()).getRemoteResourceManager(), btnOnClickListenerFocus, false);
                                mStateHolder.startTaskGetTasks(NearbyVenuesActivity.this);
                            } else {
                                sortTasksListView();
                            }
                            break;
                        default:
                            int categoryId = -1;
                            switch(position) {
                                case 2:
                                    categoryId = VenueCategoryRes.VENUE_SHOP;
                                    break;
                                case 3:
                                    categoryId = VenueCategoryRes.VENUE_FOOD;
                                    break;
                                case 4:
                                    categoryId = VenueCategoryRes.VENUE_ENTERTAINMENT;
                                    break;
                                case 5:
                                    categoryId = VenueCategoryRes.VENUE_HOTEL;
                                    break;
                            }
                            mStateHolder.setCategoryId(categoryId);
                            categoryListView();
                            break;
                    }
            }
            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });

        SegmentedButton headerButton = (SegmentedButton)findViewById(R.id.btnVenueSegmented);
        headerButton.clearButtons();
        headerButton.addButtons(
                getString(R.string.venue_score_kilometer_one),
                getString(R.string.venue_score_kilometer_two),
                getString(R.string.venue_score_kilometer_three));

        headerButton.setOnClickListener(new OnClickListenerSegmentedButton() {
            public void onClick(int index) {
                mStateHolder.setDistanceIndex(index);
                if (!mStateHolder.getIsTaskPage())
                    sortVenuesListView();
                else
                    sortTasksListView();
            }
        });

        mHandler = new Handler();
        mListView = getListView();
        mVenueListAdapter = new VenueListAdapter(this);
        mListView.setAdapter(mVenueListAdapter);
        mListView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Venue venue = (Venue) parent.getAdapter().getItem(position);
                startItemActivity(venue);
            }
        });

        // We can dynamically add a footer to our loadable listview.
        LayoutInflater inflater = LayoutInflater.from(this);
        mFooterView = (LinearLayout)inflater.inflate(R.layout.geo_loc_address_view, null);
        mTextViewFooter = (TextView)mFooterView.findViewById(R.id.footerTextView);
        LinearLayout llMain = (LinearLayout)findViewById(R.id.main);
        llMain.addView(mFooterView);

        mEmptyProgress = findViewById(R.id.emptyProgress);
        mEmptyProgress.setVisibility(ViewGroup.VISIBLE);

        getListView().setDividerHeight(0);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            Dialog dialog = new AlertDialog.Builder(this)
            .setTitle(this.getResources().getString(R.string.app_name))
            .setMessage(this.getResources().getString(R.string.exit_note))
            .setPositiveButton(this.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                    finish();
                    android.os.Process.killProcess(android.os.Process.myPid());
                }
            }).setNegativeButton(this.getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                }
            }).create();
            dialog.show();
            return true;
        }
        else
            return super.onKeyDown(keyCode, event);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mLoggedOutReceiver);
    }

    @Override
    public void onResume() {
        super.onResume();
        ((PlayLv2) getApplication()).requestLocationUpdates(mSearchLocationObserver);
    }

    @Override
    public void onPause() {
        super.onPause();
        ((PlayLv2) getApplication()).removeLocationUpdates(mSearchLocationObserver);
        if (isFinishing()) {
            mStateHolder.cancelAllTasks();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        menu.add(Menu.NONE, MENU_REFRESH, Menu.NONE, R.string.refresh)
                .setIcon(R.drawable.ic_menu_refresh);
        menu.add(Menu.NONE, MENU_SEARCH, Menu.NONE, R.string.search_label)
                .setIcon(R.drawable.ic_menu_search)
                .setAlphabeticShortcut(SearchManager.MENU_KEY);
        menu.add(Menu.NONE, MENU_ADD_VENUE, Menu.NONE, R.string.nearby_menu_add_venue)
                .setIcon(R.drawable.ic_menu_add);
        MenuUtils.addPreferencesToMenu(this, menu);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case MENU_REFRESH:
                if (!mStateHolder.getIsTaskPage()) {
                    startTask(getClearGeolocationOnSearch());
                }
                else {
                    mStateHolder.startTaskGetTasks(this);
                }
                return true;
            case MENU_SEARCH:
                showDialog(DIALOG_PICK_CATEGORY);
                return true;
            case MENU_ADD_VENUE:
                startActivity(new Intent(NearbyVenuesActivity.this, AddVenueActivity.class));
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void categoryListView() {
        int categoryId = mStateHolder.getCategoryId();
        if (categoryId > -1) {
            Group<Venue> vv = new Group<Venue>();
            for ( Venue it:mStateHolder.getVenues()) {
                if (it.getCategory().getId() == categoryId) {
                    vv.add(it);
                }
            }
            mStateHolder.setFilterVenues(vv);
        } else {
            mStateHolder.setFilterVenues(mStateHolder.getVenues());
        }
        sortVenuesListView();
    }

    private void sortVenuesListView() {
        Group<Venue> venues = mStateHolder.getFilterVenues();
        Group<Venue> venuesUpdate = new Group<Venue>();
            if (venues.size() > 0) {
                int index = mStateHolder.getDistanceIndex();
                switch (index) {
                    case 0:
                        venuesUpdate = venues;
                        break;
                    case 1:
                        for (Venue it : venues) {
                            if (Integer.valueOf(it.getDistance()) < 500) {
                                venuesUpdate.add(it);
                            }
                        }
                        break;
                    case 2:
                        for (Venue it : venues) {
                            if (Integer.valueOf(it.getDistance()) < 100) {
                                venuesUpdate.add(it);
                            }
                        }
                        break;
                }
        }
        updateVenuesListView(venuesUpdate);
    }

    private void updateVenuesListView(Group<Venue> venues)   {
        if (!mStateHolder.getIsTaskPage()) {
            if (!mStateHolder.getIsRunningTask()) {
                mEmptyProgress.setVisibility(View.GONE);
            }
            if (venues != null && venues.size() > 0) {
                mVenueListAdapter.setGroup(venues);
                mListView.setAdapter(mVenueListAdapter);
                mEmpty.setVisibility(View.GONE);
            } else {
                mListView.setAdapter(null);
                mEmpty.setVisibility(View.VISIBLE);
            }
        }
    }

    private void sortTasksListView() {
        Group<Task> tasks = mStateHolder.getTasks();
        Group<Task> tasksUpdate = new Group<Task>();
            if (tasks != null && tasks.size() > 0) {
                int index = mStateHolder.getDistanceIndex();
                switch (index) {
                    case 0:
                        tasksUpdate = tasks;
                        break;
                    case 1:
                        for (Task it : tasks) {
                            if (Integer.valueOf(it.getDistance()) < 500) {
                                tasksUpdate.add(it);
                            }
                        }
                        break;
                    case 2:
                        for (Task it : tasks) {
                            if (Integer.valueOf(it.getDistance()) < 100) {
                                tasksUpdate.add(it);
                            }
                        }
                        break;
                }
        }
        updateTasksListView(tasksUpdate);
    }

    private void updateTasksListView(Group<Task> tasks)   {
        if (mStateHolder.getIsTaskPage()) {
            if (!mStateHolder.getIsRunningGetTasks()) {
                mEmptyProgress.setVisibility(View.GONE);
            }   else {
                mEmptyProgress.setVisibility(View.VISIBLE);
            }
            if (tasks != null && tasks.size() > 0 ) {
                getListView().setAdapter(mFocusSubTripListAdapter);
                mFocusSubTripListAdapter.setGroup(tasks);
                mListView.setAdapter(mFocusSubTripListAdapter);
                mEmpty.setVisibility(View.GONE);
            } else {
                mListView.setAdapter(null);
                mEmpty.setVisibility(View.VISIBLE);
            }
        }
    }

    private void startItemActivity(Venue venue) {
        Intent intent = new Intent(NearbyVenuesActivity.this, VenueActivity.class);
        if (mStateHolder.isFullyLoadedVenue(venue.getId())) {
        	intent.putExtra(VenueActivity.INTENT_EXTRA_VENUE, venue);
        } else {
        	intent.putExtra(VenueActivity.INTENT_EXTRA_VENUE_PARTIAL, venue);
        }
        startActivityForResult(intent, RESULT_CODE_ACTIVITY_VENUE);
    }

    private void populateFooter(String reverseGeoLoc) {
        mFooterView.setVisibility(View.VISIBLE);
        mTextViewFooter.setText(reverseGeoLoc);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case RESULT_CODE_ACTIVITY_VENUE:
                if (resultCode == Activity.RESULT_OK && data.hasExtra(VenueActivity.EXTRA_VENUE_RETURNED)) {
                	Venue venue = (Venue)data.getParcelableExtra(VenueActivity.EXTRA_VENUE_RETURNED);
                	mStateHolder.updateVenue(venue);
                	mVenueListAdapter.notifyDataSetInvalidated();
                }
                break;
        }
    }

    private long getClearGeolocationOnSearch() {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
        boolean cacheGeolocation = settings.getBoolean(Preferences.PREFERENCE_CACHE_GEOLOCATION_FOR_SEARCHES, true);
        if (cacheGeolocation) {
            return 0L;
        } else {
            PlayLv2 playlv2 = ((PlayLv2) getApplication());
            playlv2.clearLastKnownLocation();
            playlv2.removeLocationUpdates(mSearchLocationObserver);
            playlv2.requestLocationUpdates(mSearchLocationObserver);
            return 2000L;
        }
    }

    private FocusSubTripListAdapter.ButtonRowClickHandler btnOnClickListenerFocus = new FocusSubTripListAdapter.ButtonRowClickHandler(){
        @Override
        public void onImageClick(Task task) {
            Bitmap bitmapOriginal = null;
            if (task.getPhotoBitmap() != null)  {
                bitmapOriginal = task.getPhotoBitmap();
            }   else {
                bitmapOriginal = BitmapFactory.decodeResource(NearbyVenuesActivity.this.getResources(), R.drawable.trip_default_photo);
            }
            if (bitmapOriginal != null) {
                ShowImageDialog dlg = new ShowImageDialog(NearbyVenuesActivity.this, bitmapOriginal,
                                                                        getWindowManager().getDefaultDisplay().getWidth());
                dlg.show();
            }
        }
        @Override
        public void onMoreClick() {
        }
    };

    /** If location changes, auto-start a nearby venues search. */
    private class SearchLocationObserver implements Observer {

        private boolean mRequestedFirstSearch = false;

        @Override
        public void update(Observable observable, Object data) {
            Location location = (Location) data;
            // Fire a search if we haven't done so yet.
            if (!mRequestedFirstSearch
                    && ((BestLocationListener) observable).isAccurateEnough(location)) {
                mRequestedFirstSearch = true;
                if (mStateHolder.getIsRunningTask() == false) {
                    // Since we were told by the system that location has changed, no need to make the
                    // task wait before grabbing the current location.
                    mHandler.post(new Runnable() {
                        public void run() {
                            startTask(0L);
                        }
                    });
                }
            }
        }
    }

    private void startTask(long geoLocDelayTimeInMs) {
        if (mStateHolder.getIsRunningTask() == false) {
            setProgressBarIndeterminateVisibility(true);
            if (mStateHolder.getVenues().size() == 0) {
                mEmptyProgress.setVisibility(ViewGroup.VISIBLE);
            }
            mStateHolder.startTask(this, mStateHolder.getQuery(), geoLocDelayTimeInMs);
        }
    }

    private void onTaskComplete(Group<Venue> venues, String reverseGeoLoc, Exception ex) {
        if (venues != null) {
            mStateHolder.setVenues(venues);
            mStateHolder.setReverseGeoLoc(reverseGeoLoc);
        } else {
            mStateHolder.setVenues(new Group<Venue>());
            NotificationsUtil.ToastReasonForFailure(NearbyVenuesActivity.this, ex);
        }
        mEmptyProgress.setVisibility(View.GONE);
        categoryListView();
        populateFooter(mStateHolder.getReverseGeoLoc());
        setProgressBarIndeterminateVisibility(false);
        mStateHolder.cancelAllTasks();
    }

    /** Handles the work of finding nearby venues. */
    private static class SearchTask extends AsyncTask<Void, Void, Group<Venue>> {

        private NearbyVenuesActivity mActivity;
        private Exception mException = null;
        private String mQuery;
        private long mSleepTimeInMs;
        private PlayLv2Api mPlayLv2Api;
        private String mReverseGeoLoc; // Filled in after execution.
        private String mNoLocException;
        private String mLabelNearby;

        public SearchTask(NearbyVenuesActivity activity, String query, long sleepTimeInMs) {
            super();
            mActivity = activity;
            mQuery = query;
            mSleepTimeInMs = sleepTimeInMs;
            mPlayLv2Api = ((PlayLv2)activity.getApplication()).getPlayLv2Api();
            mNoLocException = activity.getResources().getString(R.string.nearby_venues_no_location);
            mLabelNearby = activity.getResources().getString(R.string.nearby_venues_label_nearby);
        }

        @Override
        public void onPreExecute() {
        }

        @Override
        public Group<Venue> doInBackground(Void... params) {

            try {
                // If the user has chosen to clear their geolocation on each search, wait briefly
                // for a new fix to come in. The two-second wait time is arbitrary and can be
                // changed to something more intelligent.
                if (mSleepTimeInMs > 0L) {
                    Thread.sleep(mSleepTimeInMs);
                }

                Location location = ((PlayLv2) mActivity.getApplication()).getLastKnownLocation();
                if (location == null) {
                    throw new PlayLv2ApiException(mNoLocException);
                }

                Group<Venue> results = mPlayLv2Api.venues(LocationUtils
                        .createPlayLv2ApiLocation(location), mQuery, 30, 1000);
                // Try to get our reverse geolocation.
                mReverseGeoLoc = getGeocode(mActivity, location);
                return results;

            } catch (Exception ex) {
                mException = ex;
                return null;
            }
        }

        @Override
        public void onPostExecute(Group<Venue> groups) {
            if (mActivity != null) {
                mActivity.onTaskComplete(groups, mReverseGeoLoc, mException);
            }
        }

        private String getGeocode(Context context, Location location) {
            Geocoder geocoded = new Geocoder(context, Locale.getDefault());
            try {
                List<Address> addresses = geocoded.getFromLocation(
                        location.getLatitude(), location.getLongitude(), 1);
                if (addresses.size() > 0) {
                    Address address = addresses.get(0);

                    StringBuilder sb = new StringBuilder(128);
                    sb.append(mLabelNearby);
                    sb.append(" ");
                    sb.append(address.getAddressLine(0));
                    if (addresses.size() > 1) {
                        sb.append(", ");
                        sb.append(address.getAddressLine(1));
                    }
                    if (addresses.size() > 2) {
                        sb.append(", ");
                        sb.append(address.getAddressLine(2));
                    }

                    if (!TextUtils.isEmpty(address.getLocality())) {
                        if (sb.length() > 0) {
                            sb.append(", ");
                        }
                        sb.append(address.getLocality());
                    }

                    return sb.toString();
                }
            } catch (Exception ex) {
            }
            return null;
        }
    }

    private void onGetTasksComplete(Group<Task> tasks, Exception ex) {
        setProgressBarIndeterminateVisibility(false);
        mEmptyProgress.setVisibility(View.GONE);
        mStateHolder.setIsRunningGetTasks(false);
        if ( tasks != null && tasks.size() > 0 ) {
            mStateHolder.setTasks(tasks);
        }
        sortTasksListView();
    }

     private static class TaskGetTasks extends AsyncTask<Void, Void, Group<Task>> {

         private NearbyVenuesActivity mActivity;
         private Exception mException;
         private int mTasksPage;

         public TaskGetTasks(NearbyVenuesActivity activity) {
             mActivity = activity;
         }

         @Override
         public Group<Task> doInBackground(Void... params) {

             try {
                 Thread.sleep(2000L);
                 // Get last known location.
                 Location location = ((PlayLv2) mActivity.getApplication()).getLastKnownLocation();
                 if (location == null) {
                     throw new PlayLv2ApiException( mActivity.getResources().getString(R.string.nearby_venues_no_location));
                 }
                 return ((PlayLv2) mActivity.getApplication()).getPlayLv2Api().tasksFocused(mTasksPage);
             } catch (Exception ex) {
                 mException = ex;
                 return null;
             }
         }

         @Override
         protected void onPreExecute() {
             mActivity.onTaskStart();
         }

         @Override
         public void onPostExecute(Group<Task> tasks) {
             if (mActivity != null) {
                 mActivity.onGetTasksComplete(tasks, mException);
             }
         }
     }

     private void onTaskStart() {
         setProgressBarIndeterminateVisibility(true);
         if (mStateHolder.getTasks() == null ||
                 mStateHolder.getTasks().size() == 0) {
             mEmptyProgress.setVisibility(ViewGroup.VISIBLE);
         }
     }

    protected Dialog onCreateDialog(int id) {
        switch (id) {
            case DIALOG_PICK_CATEGORY:
                SearchDialog dlg = new SearchDialog(this);
                dlg.setOnCancelListener(new OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialog) {
                        removeDialog(DIALOG_PICK_CATEGORY);
                    }
                });
                return dlg;

            case DIALOG_ERROR:
                break;
        }
        return null;
    }

    private static class StateHolder {
        private Group<Venue> mVenues;
        private Group<Venue> mFilterVenues;
        private Group<Task> mTasks;
        private String mQuery;
        private String mReverseGeoLoc;
        private SearchTask mSearchTask;
        private TaskGetTasks mTaskGetTasks;
        private Set<String> mFullyLoadedVenueIds;
        private int mCategoryId;
        private int mDistanceIndex;
        private boolean mIsTaskPage;
        private boolean mIsRunningGetTasks;
        private boolean mIsRunnedGetTasks;

        public StateHolder() {
            mVenues = new Group<Venue>();
            mFilterVenues = mVenues;
            mSearchTask = null;
            mFullyLoadedVenueIds = new HashSet<String>();
            mDistanceIndex = 0;
            mCategoryId = -1;
            mIsTaskPage = false;
            mIsRunningGetTasks = false;
            mIsRunnedGetTasks = false;
        }

        public boolean getIsRunnedGetTasks() {
            return mIsRunnedGetTasks;
        }

        public void setIsRunnedGetTasks(boolean isRunned) {
            mIsRunnedGetTasks = isRunned;
        }

        public boolean getIsRunningGetTasks() {
            return mIsRunningGetTasks;
        }

        public void setIsRunningGetTasks(boolean isRunning) {
            mIsRunningGetTasks = isRunning;
        }

        public boolean getIsTaskPage()  {
            return mIsTaskPage;
        }

        public void setIsTaskPage(boolean isTaskPage)  {
            mIsTaskPage = isTaskPage;
        }

        public int getCategoryId() {
            return mCategoryId;
        }

        public void setCategoryId(int id)   {
            mIsTaskPage = false;
            mCategoryId = id;
        }

        public int getDistanceIndex() {
            return mDistanceIndex;
        }

        public void setDistanceIndex(int index)   {
            mDistanceIndex = index;
        }

        public Group<Venue> getFilterVenues()   {
            return mFilterVenues;
        }

        public void setFilterVenues(Group<Venue> venues) {
            mFilterVenues = venues;
        }

        public String getQuery() {
            return mQuery;
        }

        public void setQuery(String query) {
            mQuery = query;
        }

        public String getReverseGeoLoc() {
            return mReverseGeoLoc;
        }

        public void setReverseGeoLoc(String reverseGeoLoc) {
            mReverseGeoLoc = reverseGeoLoc;
        }

        public Group<Venue> getVenues() {
            return mVenues;
        }

        public void setVenues(Group<Venue> venues) {
            mVenues = venues;
        }

        public Group<Task> getTasks()   {
            return mTasks;
        }

        public void setTasks(Group<Task> tasks) {
            mTasks = tasks;
        }

        public void startTaskGetTasks(NearbyVenuesActivity activity) {
            if ( !mIsRunningGetTasks ) {
                mTaskGetTasks = new TaskGetTasks(activity);
                mTaskGetTasks.execute();
                mIsRunningGetTasks = true;
                mIsRunnedGetTasks = true;
            }
        }

        public void startTask(NearbyVenuesActivity activity, String query, long sleepTimeInMs) {
            mSearchTask = new SearchTask(activity, query, sleepTimeInMs);
            mSearchTask.execute();
        }

        public boolean getIsRunningTask() {
            return mSearchTask != null;
        }

        public void cancelAllTasks() {
            if (mSearchTask != null) {
                mSearchTask.cancel(true);
                mSearchTask = null;
            }
        }

        public void updateVenue(Venue venue) {
        	for (Venue it : mVenues) {
    			if (it.getId().equals(venue.getId())) {
    				// The /venue api call does not supply the venue's distance value,
    				// so replace it manually here.
    				Venue replaced = it;
    				Venue replacer = VenueUtils.cloneVenue(venue);
    				replacer.setDistance(replaced.getDistance());
    				it = replacer;
    				mFullyLoadedVenueIds.add(replacer.getId());
        		}
        	}
        }

        public boolean isFullyLoadedVenue(String vid) {
        	return mFullyLoadedVenueIds.contains(vid);
        }
    }
}
